define('oj/gamelib', ['oj/three', 'oj/box2d', 'oj/gameutils'], function (THREE, Box2D, gameutils) {
    /**
     * gamelib - a webgl game library
     */
    
    var gamelib = {
        /*3rd party libraries*/
        THREE                 : THREE,
        Box2D                 : Box2D,
        
        /*Constants*/
        PHYSICS_DT            : 1 / 60,
        PI180                 : Math.PI / 180,
        DEFAULT_DENSITY       : 1,
        DEFAULT_FRICTION      : 0.1,
        DEFAULT_RESTITUTION   : 0.8,
        
        /*Objects*/
        Object                : Object,                // Base object
        Mesh                  : Mesh,                  // An Object with visual and optional physical presence
        Particles             : Particles,             // An Object to manage particles
        
        /*Functions*/
        createMaterial        : createMaterial,        //(type, parameters)
        createGround          : createGround,          //(width, depth, material)
        createBox             : createBox,             //(x, y, z, width, height, depth, material, properties)
        createAmbientLight    : createAmbientLight,    //(hex)
        createDirectionalLight: createDirectionalLight,//(x, y, z, hex, intensity)
        createPointLight      : createPointLight,      //(x, y, z, hex, intensity)
        createSpotLight       : createSpotLight,       //(x, y, z, targetX, targetY, targetZ, hex)
        play                  : play,                  //()
        
        /*Properties*/
        stats                 : new gameutils.Stats,
        gui                   : null,
        camera                : new THREE.Camera(33, window.innerWidth / window.innerHeight, 3, 3000),
        world                 : new Box2D.Dynamics.b2World(new Box2D.Common.Math.b2Vec2(0, 0), true),
        scene                 : new THREE.Scene,
        renderer              : new THREE.WebGLRenderer({ clearColor: 0x000000, clearAlpha: 1, antialias: false }),
        objects               : [],
        object                : 0,
        time                  : 0
    };
    
    
    
    var SHADOW_MAP_WIDTH = 1024,
        SHADOW_MAP_HEIGHT = 1024;
    
    
    // define and the game loop
    function loop (time) {
        /**
         * Physics update and render loop.
         */
        gamelib.world.Step(gamelib.PHYSICS_DT, 10, 10);
        gamelib.world.ClearForces();
        gamelib.time = time;
        gamelib.object = gamelib.objects.length;
        while (gamelib.object--) {
            gamelib.objects[gamelib.object].update(time);
        }
        requestAnimationFrame(loop);
        gamelib.renderer.clear();
        gamelib.renderer.render(gamelib.scene, gamelib.camera);
        gamelib.stats.update();
    }
    
    
    
    function play() {
        /**
         * Call once when ready to start the game loop.
         */
        
        gamelib.scene.fog = new THREE.Fog(0xffffff, gamelib.camera.far - 1000, gamelib.camera.far);
        
        gamelib.renderer.setSize(window.innerWidth, window.innerHeight);
        
        gamelib.renderer.setClearColor(gamelib.scene.fog.color, 1);
        gamelib.renderer.autoClear = false;
        
        gamelib.renderer.shadowCameraNear = 1;
        gamelib.renderer.shadowCameraFar = gamelib.camera.far;
        gamelib.renderer.shadowCameraFov = gamelib.camera.fov;
        gamelib.renderer.shadowMapBias = 0.0039;
        gamelib.renderer.shadowMapDarkness = 0.5;
        gamelib.renderer.shadowMapWidth = SHADOW_MAP_WIDTH;
        gamelib.renderer.shadowMapHeight = SHADOW_MAP_HEIGHT;
        gamelib.renderer.shadowMapEnabled = true;
        gamelib.renderer.shadowMapSoft = true;
        
        gamelib.stats.domElement.style.position = 'absolute';
        gamelib.stats.domElement.style.top = '0px';
        
        document.body.style.margin = '0';
        document.body.style.overflow = 'hidden';
        
        document.body.appendChild(gamelib.renderer.domElement);
        document.body.appendChild(gamelib.stats.domElement);
        
        gamelib.gui = new gameutils.GUI;
        loop((new Date).getTime());
    }
    
    
    
    function Object() {}
    Object.prototype.constructor = Object;
    
    Object.prototype.init = function (geometry, material) {
        this.created = (new Date).getTime();
        this.geometry = geometry;
        this.material = material;
        
        if (this.material.map) {
            this.material.map.needsUpdate = true;
        }
        
        gamelib.objects.push(this);
        
        return this;
    };
    
    Object.prototype.update = function (time) {
        /**
         * If the Object is dynamic, this function gets called once every game loop.
         */
        
    };
    
    
    
    function Mesh() {}
    Mesh.prototype = new Object;
    Mesh.prototype.constructor = Mesh;
    
    Mesh.prototype.init = function (x, y, z, geometry, material, fixture, dynamic) {
        gamelib.Object.prototype.init.call(this, geometry, material);
        
        this.mesh = new gamelib.THREE.Mesh(this.geometry, this.material);
        this.mesh.position.set(x, y, z);
        this.mesh.castShadow = true;
        this.mesh.receiveShadow = true;
        
        if (typeof fixture !== 'undefined') {
            var bodydef = new gamelib.Box2D.Dynamics.b2BodyDef;
            if (typeof dynamic !== 'undefined' && dynamic === true) {
                bodydef.type = gamelib.Box2D.Dynamics.b2Body.b2_dynamicBody;
            } else {
                bodydef.type = gamelib.Box2D.Dynamics.b2Body.b2_staticBody;
            }
            bodydef.position.Set(x, z);
            this.body = gamelib.world.CreateBody(bodydef);
            this.body.CreateFixture(fixture);
        }
        
        gamelib.scene.addObject(this.mesh);
        
        return this;
    };
    
    Mesh.prototype.update = function (time) {
        /**
         * Overrides gamelib.Object.update.
         */
        
        this.mesh.position.x = this.body.m_xf.position.x;
        this.mesh.position.z = this.body.m_xf.position.y;
        this.mesh.rotation.y = this.body.GetAngle();
    };
    
    
    
    function Particles() {}
    Particles.prototype = new Object;
    Particles.prototype.constructor = Particles;
    
    Particles.prototype.init = function (x, y, z, width, height, depth, properties) {
        
        var geometry = new gamelib.THREE.Geometry(),
            material = gamelib.createMaterial('ParticleBasic', properties);
        
        gamelib.Object.prototype.init.call(this, geometry, material);
        
        this.xbound = width / 2;
        this.ybound = height / 2;
        this.zbound = depth / 2;
        
        this.object = new gamelib.THREE.ParticleSystem(geometry, material);
        //this.object.sortParticles = true;
        //this.object.dynamic = true;
        //this.object.updateMatrix();
        
        gamelib.scene.addChild(this.object);
        
        return this;
        
    };
    
    Particles.prototype.addParticle = function (x, y, z, hex) {
        var particle = new gamelib.THREE.Vertex(new gamelib.THREE.Vector3(x, y, z));
        
        //this.i = this.object.geometry.vertices.length;
        //while (this.i--) {
        //    geometry.vertices.push(this.object.geometry.vertices[this.i]);
        //    geometry.colors.push(this.object.geometry.colors[this.i]);
        //}
        //this.geometry = geometry;
        
        this.geometry.vertices.push(particle);
        this.geometry.colors.push(new gamelib.THREE.Color(hex));
        
        gamelib.scene.removeChild(this.object);
        this.object = new gamelib.THREE.ParticleSystem(this.geometry, this.material);
        /**
 * gamelib - a webgl game library
 */

define('gamelib', ['three', 'box2d', 'gameutils'], function (THREE, Box2D, gameutils) {
    'use strict';
    
    var gamelib = {
        /*3rd party libraries*/
        THREE                 : THREE,
        Box2D                 : Box2D,
        
        /*Constants*/
        PHYSICS_DT            : 1 / 60,
        PI180                 : Math.PI / 180,
        DEFAULT_DENSITY       : 1,
        DEFAULT_FRICTION      : 0.1,
        DEFAULT_RESTITUTION   : 0.8,
        
        /*Objects*/
        Object                : Object,                // Base object
        Mesh                  : Mesh,                  // An Object with visual and optional physical presence
        Particles             : Particles,             // An Object to manage particles
        
        /*Functions*/
        createMaterial        : createMaterial,        //(type, parameters)
        createGround          : createGround,          //(width, depth, material)
        createBox             : createBox,             //(x, y, z, width, height, depth, material, properties)
        createAmbientLight    : createAmbientLight,    //(hex)
        createDirectionalLight: createDirectionalLight,//(x, y, z, hex, intensity)
        createPointLight      : createPointLight,      //(x, y, z, hex, intensity)
        createSpotLight       : createSpotLight,       //(x, y, z, targetX, targetY, targetZ, hex)
        play                  : play,                  //()
        
        /*Properties*/
        stats                 : new gameutils.Stats,
        gui                   : null,
        camera                : new THREE.Camera(33, window.innerWidth / window.innerHeight, 3, 3000),
        world                 : new Box2D.Dynamics.b2World(new Box2D.Common.Math.b2Vec2(0, 0), true),
        scene                 : new THREE.Scene,
        renderer              : new THREE.WebGLRenderer({ clearColor: 0x000000, clearAlpha: 1, antialias: false }),
        objects               : [],
        object                : 0,
        time                  : 0
    };
    
    
    
    var SHADOW_MAP_WIDTH = 1024,
        SHADOW_MAP_HEIGHT = 1024;
    
    
    // define and the game loop
    function loop (time) {
        /**
         * Physics update and render loop.
         */
        gamelib.world.Step(gamelib.PHYSICS_DT, 10, 10);
        gamelib.world.ClearForces();
        gamelib.time = time;
        gamelib.object = gamelib.objects.length;
        while (gamelib.object--) {
            gamelib.objects[gamelib.object].update(time);
        }
        requestAnimationFrame(loop);
        gamelib.renderer.clear();
        gamelib.renderer.render(gamelib.scene, gamelib.camera);
        gamelib.stats.update();
    }
    
    
    
    function play() {
        /**
         * Call once when ready to start the game loop.
         */
        
        gamelib.scene.fog = new THREE.Fog(0xffffff, gamelib.camera.far - 1000, gamelib.camera.far);
        
        gamelib.renderer.setSize(window.innerWidth, window.innerHeight);
        
        gamelib.renderer.setClearColor(gamelib.scene.fog.color, 1);
        gamelib.renderer.autoClear = false;
        
        gamelib.renderer.shadowCameraNear = 1;
        gamelib.renderer.shadowCameraFar = gamelib.camera.far;
        gamelib.renderer.shadowCameraFov = gamelib.camera.fov;
        gamelib.renderer.shadowMapBias = 0.0039;
        gamelib.renderer.shadowMapDarkness = 0.5;
        gamelib.renderer.shadowMapWidth = SHADOW_MAP_WIDTH;
        gamelib.renderer.shadowMapHeight = SHADOW_MAP_HEIGHT;
        gamelib.renderer.shadowMapEnabled = true;
        gamelib.renderer.shadowMapSoft = true;
        
        gamelib.stats.domElement.style.position = 'absolute';
        gamelib.stats.domElement.style.top = '0px';
        
        document.body.style.margin = '0';
        document.body.style.overflow = 'hidden';
        
        document.body.appendChild(gamelib.renderer.domElement);
        document.body.appendChild(gamelib.stats.domElement);
        
        gamelib.gui = new gameutils.GUI;
        loop((new Date).getTime());
    }
    
    
    
    function Object() {}
    Object.prototype.constructor = Object;
    
    Object.prototype.init = function (geometry, material) {
        this.created = (new Date).getTime();
        this.geometry = geometry;
        this.material = material;
        
        if (this.material.map) {
            this.material.map.needsUpdate = true;
        }
        
        gamelib.objects.push(this);
        
        return this;
    };
    
    Object.prototype.update = function (time) {
        /**
         * If the Object is dynamic, this function gets called once every game loop.
         */
        
    };
    
    
    
    function Mesh() {}
    Mesh.prototype = new Object;
    Mesh.prototype.constructor = Mesh;
    
    Mesh.prototype.init = function (x, y, z, geometry, material, fixture, dynamic) {
        gamelib.Object.prototype.init.call(this, geometry, material);
        
        this.mesh = new gamelib.THREE.Mesh(this.geometry, this.material);
        this.mesh.position.set(x, y, z);
        this.mesh.castShadow = true;
        this.mesh.receiveShadow = true;
        
        if (typeof fixture !== 'undefined') {
            var bodydef = new gamelib.Box2D.Dynamics.b2BodyDef;
            if (typeof dynamic !== 'undefined' && dynamic === true) {
                bodydef.type = gamelib.Box2D.Dynamics.b2Body.b2_dynamicBody;
            } else {
                bodydef.type = gamelib.Box2D.Dynamics.b2Body.b2_staticBody;
            }
            bodydef.position.Set(x, z);
            this.body = gamelib.world.CreateBody(bodydef);
            this.body.CreateFixture(fixture);
        }
        
        gamelib.scene.addObject(this.mesh);
        
        return this;
    };
    
    Mesh.prototype.update = function (time) {
        /**
         * Overrides gamelib.Object.update.
         */
        
        this.mesh.position.x = this.body.m_xf.position.x;
        this.mesh.position.z = this.body.m_xf.position.y;
        this.mesh.rotation.y = this.body.GetAngle();
    };
    
    
    
    function Particles() {}
    Particles.prototype = new Object;
    Particles.prototype.constructor = Particles;
    
    Particles.prototype.init = function (x, y, z, width, height, depth, properties) {
        
        var geometry = new gamelib.THREE.Geometry(),
            material = gamelib.createMaterial('ParticleBasic', properties);
        
        gamelib.Object.prototype.init.call(this, geometry, material);
        
        this.xbound = width / 2;
        this.ybound = height / 2;
        this.zbound = depth / 2;
        
        this.object = new gamelib.THREE.ParticleSystem(geometry, material);
        this.object.sortParticles = true;
        //this.object.dynamic = true;
        //this.object.updateMatrix();
        
        gamelib.scene.addChild(this.object);
        
        return this;
        
    };
    
    Particles.prototype.addParticle = function (x, y, z, hex) {
        var particle = new gamelib.THREE.Vertex(new gamelib.THREE.Vector3(x, y, z));
        
        this.geometry.vertices.push(particle);
        this.geometry.colors.push(new gamelib.THREE.Color(hex));
        
        gamelib.scene.removeChild(this.object);
        this.object = new gamelib.THREE.ParticleSystem(this.geometry, this.material);
        this.object.updateMatrix();
        gamelib.scene.addChild(this.object);
        
        return particle;
    };
    
    Particles.prototype.update = function (time) {
        /**
         * Overrides gamelib.Object.update.
         */
        
        this.i = this.object.geometry.vertices.length;
        while (this.i--) {
            this.particle = this.object.geometry.vertices[this.i];
            this.particle.position.addSelf(this.particle.velocity);
            if (this.particle.position.x > this.xbound || this.particle.position.x < -this.xbound) {
                this.particle.position.x = 0;
            }
            if (this.particle.position.y > this.ybound || this.particle.position.y < -this.ybound) {
                this.particle.position.y = 0;
            }
            if (this.particle.position.z > this.zbound || this.particle.position.z < -this.zbound) {
                this.particle.position.z = 0;
            }
        }
        this.object.updateMatrix();
    };
    
    
    
    function createMaterial (type, parameters) {
        /**
         * Return a new material instances.
         */
        
        if (type === 'Lambert') {
            return new gamelib.THREE.MeshLambertMaterial(parameters);
        } else if (type === 'Depth') {
            return new gamelib.THREE.MeshDepthMaterial(parameters);
        } else if (type === 'Normal') {
            return new gamelib.THREE.MeshNormalMaterial(parameters);
        } else if (type === 'Phong') {
            return new gamelib.THREE.MeshPhongMaterial(parameters);
        } else if (type === 'Shader') {
            return new gamelib.THREE.MeshShaderMaterial(parameters);
        } else if (type === 'ParticleBasic') {
            return new gamelib.THREE.ParticleBasicMaterial(parameters);
        }
        
        return new gamelib.THREE.MeshBasicMaterial(parameters);
    }
    
    
    
    function createGround (width, depth, material) {
        /**
         * Adds a cube 1 height with width and depth at (0, 0, 0) to the world.
         *
         * The cube has not physical presences.
         */
        
        var mesh = new THREE.Mesh(new THREE.CubeGeometry(width, 1, depth), material);
        
        mesh.position.set(0, 0, 0);
        mesh.scale.set(100, 1, 100);
        
        mesh.castShadow = false;
        mesh.receiveShadow = true;
        
        gamelib.scene.addObject(mesh);
    }
    
    
    
    function createBox (x, y, z, width, height, depth, material, properties) {
        /**
         * Adds a gamelib.Object to the world that has the physical and visual properties of a box.
         *
         * If properties defines 'dynamic':true the object is affected by surrounding forces and
         * receives update() calls. Physical properties that can be defines are density, friction
         * and restitution.
         */
        
        var geometry = new THREE.CubeGeometry(width, height, depth),
            fixture = new Box2D.Dynamics.b2FixtureDef;
        
        properties = properties || {};
        if (typeof properties.dynamic === 'undefined') {
            properties.dynamic = true;
        }
        
        fixture.density = properties.density || gamelib.DEFAULT_DENSITY;
        fixture.friction = properties.friction || gamelib.DEFAULT_FRICTION;
        fixture.restitution = properties.restitution || gamelib.DEFAULT_RESTITUTION;
        
        fixture.shape = new gamelib.Box2D.Collision.Shapes.b2PolygonShape;
        fixture.shape.SetAsBox(width * 0.5, depth * 0.5);
        
        return (new Mesh).init(x, y, z, geometry, material, fixture, properties.dynamic);
    };
    
    
    
    function createDirectionalLight (x, y, z, hex) {
        /**
         * Adds a THREE.DirectionalLight to the world.
         */
        
        var light = new THREE.DirectionalLight(hex);
        light.position.set(x, y, z);
        light.position.normalize();
        light.castShadow = true;
        gamelib.scene.addLight(light);
        return light;
    };
    
    
    
    function createPointLight (x, y, z, hex, intensity) {
        /**
         * Adds a THREE.PointLight to the world.
         */
        
        var light = new gamelib.THREE.PointLight(hex, intensity);
        light.position.set(x, y, z);
        gamelib.scene.addLight(light);
        return light;
    }
    
    
    
    function createSpotLight(x, y, z, targetX, targetY, targetZ, hex) {
        /**
         * Adds a THREE.SpotLight to the world.
         */
        
        var light = new THREE.SpotLight(hex);
        light.position.set(x, y, z);
        light.target.position.set(targetX, targetY, targetZ);
        light.castShadow = true;
        gamelib.scene.addLight(light);
        return light;
    }
    
    
    
    function createAmbientLight(hex) {
        /**
         * Adds a THREE.AmbientLight to the world.
         */
        
        var light = new THREE.AmbientLight(hex);
        gamelib.scene.addLight(light);
        return light;
    }
    
    
    
    return gamelib;
});

        
        return particle;
    };
    
    Particles.prototype.update = function (time) {
        /**
         * Overrides gamelib.Object.update.
         */
        
        this.i = this.object.geometry.vertices.length;
        while (this.i--) {
            this.particle = this.object.geometry.vertices[this.i];
            this.particle.position.addSelf(this.particle.velocity);
            if (this.particle.position.x > this.xbound || this.particle.position.x < -this.xbound) {
                this.particle.position.x = 0;
            }
            if (this.particle.position.y > this.ybound || this.particle.position.y < -this.ybound) {
                this.particle.position.y = 0;
            }
            if (this.particle.position.z > this.zbound || this.particle.position.z < -this.zbound) {
                this.particle.position.z = 0;
            }
        }
        this.object.updateMatrix();
    };
    
    
    
    function createMaterial (type, parameters) {
        /**
         * Return a new material instances.
         */
        
        if (type === 'Lambert') {
            return new gamelib.THREE.MeshLambertMaterial(parameters);
        } else if (type === 'Depth') {
            return new gamelib.THREE.MeshDepthMaterial(parameters);
        } else if (type === 'Normal') {
            return new gamelib.THREE.MeshNormalMaterial(parameters);
        } else if (type === 'Phong') {
            return new gamelib.THREE.MeshPhongMaterial(parameters);
        } else if (type === 'Shader') {
            return new gamelib.THREE.MeshShaderMaterial(parameters);
        } else if (type === 'ParticleBasic') {
            return new gamelib.THREE.ParticleBasicMaterial(parameters);
        }
        
        return new gamelib.THREE.MeshBasicMaterial(parameters);
    }
    
    
    
    function createGround (width, depth, material) {
        /**
         * Adds a cube 1 height with width and depth at (0, 0, 0) to the world.
         *
         * The cube has not physical presences.
         */
        
        var mesh = new THREE.Mesh(new THREE.CubeGeometry(width, 1, depth), material);
        
        mesh.position.set(0, 0, 0);
        mesh.scale.set(100, 1, 100);
        
        mesh.castShadow = false;
        mesh.receiveShadow = true;
        
        gamelib.scene.addObject(mesh);
    }
    
    
    
    function createBox (x, y, z, width, height, depth, material, properties) {
        /**
         * Adds a gamelib.Object to the world that has the physical and visual properties of a box.
         *
         * If properties defines 'dynamic':true the object is affected by surrounding forces and
         * receives update() calls. Physical properties that can be defines are density, friction
         * and restitution.
         */
        
        var geometry = new THREE.CubeGeometry(width, height, depth),
            fixture = new Box2D.Dynamics.b2FixtureDef;
        
        properties = properties || {};
        if (typeof properties.dynamic === 'undefined') {
            properties.dynamic = true;
        }
        
        fixture.density = properties.density || gamelib.DEFAULT_DENSITY;
        fixture.friction = properties.friction || gamelib.DEFAULT_FRICTION;
        fixture.restitution = properties.restitution || gamelib.DEFAULT_RESTITUTION;
        
        fixture.shape = new gamelib.Box2D.Collision.Shapes.b2PolygonShape;
        fixture.shape.SetAsBox(width * 0.5, depth * 0.5);
        
        return (new Mesh).init(x, y, z, geometry, material, fixture, properties.dynamic);
    };
    
    
    
    function createDirectionalLight (x, y, z, hex) {
        /**
         * Adds a THREE.DirectionalLight to the world.
         */
        
        var light = new THREE.DirectionalLight(hex);
        light.position.set(x, y, z);
        light.position.normalize();
        light.castShadow = true;
        gamelib.scene.addLight(light);
        return light;
    };
    
    
    
    function createPointLight (x, y, z, hex, intensity) {
        /**
         * Adds a THREE.PointLight to the world.
         */
        
        var light = new gamelib.THREE.PointLight(hex, intensity);
        light.position.set(x, y, z);
        gamelib.scene.addLight(light);
        return light;
    }
    
    
    
    function createSpotLight(x, y, z, targetX, targetY, targetZ, hex) {
        /**
         * Adds a THREE.SpotLight to the world.
         */
        
        var light = new THREE.SpotLight(hex);
        light.position.set(x, y, z);
        light.target.position.set(targetX, targetY, targetZ);
        light.castShadow = true;
        gamelib.scene.addLight(light);
        return light;
    }
    
    
    
    function createAmbientLight(hex) {
        /**
         * Adds a THREE.AmbientLight to the world.
         */
        
        var light = new THREE.AmbientLight(hex);
        gamelib.scene.addLight(light);
        return light;
    }
    
    
    
    return gamelib;
    
});
